#include "ULCore_Statistic.h"
#include <iomanip>

//============ULC_BallFrequency================
ULC_BallFrequency::ULC_BallFrequency():number(0), appearedTimes(0),rate(0)
{
}

void ULC_BallFrequency::CaculateRate(int totalTimes)
{
	if (appearedTimes == 0 || totalTimes == 0){
		rate = 0.0f;
		return ;
	}

	try{
		rate = static_cast<float>(appearedTimes) / static_cast<float>(totalTimes) * 100;
	}catch(float){
		cout << "Fatal error: Float exception (" << static_cast<float>(appearedTimes) << "/" << static_cast<float>(totalTimes) << ")" << endl;
		exit(1);
	}
#ifdef ULC_STATISTIC_DEBUG
	cout << __func__ << ": Caculated rate is " << rate  << endl;
#endif
}

bool ULC_BallFrequency::operator==(const ULC_BallFrequency &ballFreq)
{
	return (number == ballFreq.number);
}

bool ULC_BallFrequency::operator>(const ULC_BallFrequency &ballFreq)
{
	return (number > ballFreq.number);
}

bool ULC_BallFrequency::operator<(const ULC_BallFrequency &ballFreq)
{
	return (number < ballFreq.number);
}

//=============ULC_PositionInfo==================
ULC_BallFrequency ULC_PositionInfo::PopLowestRate(list<ULC_BallFrequency> &destList)
{
#define FLOAT_PRECISION		0.0001f
	ULC_BallFrequency tmpBall;

	if (destList.size() == 1){
		tmpBall = destList.front();
		goto out;
	}

	tmpBall = destList.front();
	iter = destList.begin();
	advance(iter, 1);
	
	while (iter != destList.end()){

		if ((tmpBall.rate - (*iter).rate) > FLOAT_PRECISION){
			tmpBall = *iter;
		}

		iter++;
	}

out:
	destList.remove(tmpBall);

	return tmpBall;
}

void ULC_PositionInfo::ProcessNumber(int number)
{
	bool foundNumber = false;

	for (iter = numberOrder.begin(); iter != numberOrder.end(); iter++){
		if ((*iter).number == number){
			foundNumber = true;
			break;
		}
	}

	if (foundNumber){
		//Number exists
		(*iter).appearedTimes++;
	}else{
		//Not found, insert this number
		ULC_BallFrequency newBall;

		newBall.number = number;
		newBall.appearedTimes = 1;
		numberOrder.push_back(newBall);
	}
}

ULC_BallFrequency ULC_PositionInfo::GetLowestRate()
{
	ULC_BallFrequency fake;

	if (rateOrder.empty()){
		return fake;
	}

	return rateOrder.front();  
}

ULC_BallFrequency ULC_PositionInfo::GetHighestRate()
{
	ULC_BallFrequency fake;

	if (rateOrder.empty()){
		return fake;
	}

	return rateOrder.back();
}

ULC_BallFrequency ULC_PositionInfo::GetMinNumber()
{
	ULC_BallFrequency fake;

	if (numberOrder.empty()){
		return fake;
	}

	return numberOrder.front();
}

ULC_BallFrequency ULC_PositionInfo::GetMaxNumber()
{
	ULC_BallFrequency fake;

	if (numberOrder.empty()){
		return fake;
	}

	return numberOrder.back();
}

int ULC_PositionInfo::GetLowestRates(list<ULC_BallFrequency> &lowestRates, int &count)
{
	int i = 0;

	if (rateOrder.empty() || count <= 0){
		count = 0;
		return -1;
	}

	if (count > rateOrder.size()){
		count = rateOrder.size();
	}

	lowestRates.clear();

	iter = rateOrder.begin();

	while (i < count){
		lowestRates.push_back(*iter);
		iter++;
		i++;
	}

	return 0;
}

int ULC_PositionInfo::GetHighestRates(list<ULC_BallFrequency> &highestRates, int &count)
{
	int i = 0;

	if (rateOrder.empty() || count <= 0){
		count = 0;
		return -1;
	}

	if (count > rateOrder.size()){
		count = rateOrder.size();
	}

	iter = rateOrder.end();
	iter--;

	while (i < count){
		highestRates.push_back(*iter);
		iter--;
		i++;		
	}

	return 0;
}

int ULC_PositionInfo::GetMinNumbers(list<ULC_BallFrequency> &minNumbers, int &count)
{
	int i = 0;

	if (numberOrder.empty() || count <= 0){
		count = 0;
		return -1;
	}

	if (count > numberOrder.size()){
		count = numberOrder.size();
	}

	minNumbers.clear();

	iter = numberOrder.begin();

	while (i < count){
		minNumbers.push_back(*iter);
		iter++;
		i++;
	}

	return 0;
}

int ULC_PositionInfo::GetMaxNumbers(list<ULC_BallFrequency> &maxNumbers, int &count)
{
	int i = 0;

	if (numberOrder.empty() || count <= 0){
		count = 0;
		return -1;
	}

	if (count > numberOrder.size()){
		count = numberOrder.size();
	}

	maxNumbers.clear();

	iter = numberOrder.end();
	iter--;

	while (i < count){
		maxNumbers.push_back(*iter);
		iter--;
		i++;
	}

	return 0;
}

int ULC_PositionInfo::Sort()
{
	list<ULC_BallFrequency> tmpList;
	ULC_BallFrequency tmpBall;

	numberOrder.sort();
	tmpList = numberOrder;

	rateOrder.clear();
	//Find lowest rate ball
	while (!tmpList.empty()){
		tmpBall = PopLowestRate(tmpList);
		rateOrder.push_back(tmpBall);
	}
}

void ULC_PositionInfo::SetupRates(int totalSerials)
{

#ifdef ULC_STATISTIC_DEBUG
	cout << __func__ << ": Total Serials is " << totalSerials << endl; 
#endif
	if (totalSerials == 0.0f){
		return ;
	}

	for (iter = numberOrder.begin(); iter != numberOrder.end(); iter++){
		ULC_BallFrequency &tmpBall = *iter;
		tmpBall.CaculateRate(totalSerials);	
#ifdef ULC_STATISTIC_DEBUG
		cout << tmpBall.number << " + " << tmpBall.appearedTimes <<  " + " << tmpBall.rate;
#endif
	}

#ifdef ULC_STATISTIC_DEBUG
	cout << endl;
#endif
}

ostream& operator<<(ostream &os, ULC_PositionInfo &posInfo)
{
	int counter = 0;
	ULC_BallFrequency ball;
	list<ULC_BallFrequency> &info = posInfo.numberOrder;
	list<ULC_BallFrequency>::iterator iter;

	os << "There are " << info.size() << " elements" << endl;

	ball = posInfo.GetMinNumber();
	os << "Minimum number is " << ball.number << endl;
	ball = posInfo.GetMaxNumber();
	os << "Maximum number is " << ball.number << endl;
	ball = posInfo.GetLowestRate();
	os << "Lowest rate is " << ball.number << endl;
	ball = posInfo.GetHighestRate();
	os << "Highest rate is " << ball.number << endl;

	os.fill('0');
	for (iter = info.begin(); iter != info.end(); iter++){
		ball = *iter;
		os << "(" << setw(2) << ball.number << "," << setw(4) << ball.appearedTimes << ","  << setw(8) << (float)ball.rate << "%) ";
		counter++;
		
		if (counter % 5 == 0){
			os << endl;
		}
	}

	os.fill(' ');

	os << endl;

	return os;
}

//=======================ULC_Statistic=====================
ULC_Statistic::ULC_Statistic():totalSerials(0)
{
}

ULC_Statistic::ULC_Statistic(ULC_History &historyData)
{
	ParseHistoryData(historyData);
}

ULC_Statistic::ULC_Statistic(int nSerials, ULC_Serial *serials)
{
	ParseSerials(nSerials, serials);
}

int ULC_Statistic::ParseSerial(ULC_Serial &serial)
{
	ULC_Balls balls;
	ULC_Balls ballsOut;

	balls = serial.sortedBalls;
	ballsOut = serial.actualOutBalls;
	
#ifdef ULC_STATISTIC_DEBUG
	cout << "ULC_Statistic --> " << __func__ << ": Processing serial " << serial.sn << endl;
#endif
	//Deal with reds
	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		redsStatistic[i].ProcessNumber(balls.reds[i]);
		redsStatisticOut[i].ProcessNumber(ballsOut.reds[i]);
	}

	//Deal with blue
	blueStatistic.ProcessNumber(balls.blue);

	return 0;
}

int ULC_Statistic::ParseHistoryData(ULC_History &historyData)
{
	return ParseSerials(historyData.totalSerials, historyData.serials);
}

int ULC_Statistic::ParseSerials(int nSerials, ULC_Serial *serials)
{
	int i;

	if (nSerials <= 0){
		totalSerials = 0;
		return -1;
	}

	for (i = 0; i < nSerials; i++){
		if ( ParseSerial(serials[i]) == -1){
			break;
		}
	}

	if (i != nSerials){
		//Error occured during dealing with serials[i]
		totalSerials = i;
	}else{
		totalSerials = nSerials;
	}

	//Cacaulate rates
	for (i = 0; i < UL_RED_BALLS_COUNT; i++){
		redsStatistic[i].SetupRates(totalSerials);
		redsStatisticOut[i].SetupRates(totalSerials);
	}

	blueStatistic.SetupRates(totalSerials);

#ifdef ULC_STATISTIC_DEBUG
	cout << __func__ << ":Before sort " << endl;
	cout << "Position 0 begin: " <<  redsStatistic[0].numberOrder.front().rate << endl;
#endif
	//Sort
	for (i = 0; i < UL_RED_BALLS_COUNT; i++){
		redsStatistic[i].Sort();
		redsStatisticOut[i].Sort();
	}

	blueStatistic.Sort();

#ifdef ULC_STATISTIC_DEBUG
	cout << __func__ << ":After sort " << endl;
	cout << "Position 0 begin: " <<  redsStatistic[0].numberOrder.front().rate << endl;
#endif
	return 0;
}

ostream& operator<<(ostream &os, ULC_Statistic &stat)
{
	ULC_BallFrequency ball;
	list<ULC_BallFrequency>::iterator iter;

	os << "<-----------Union Lotto Statistic--------->" << endl;

	//Deal with reds
	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		os << "----------------------------------------------" << endl;
		os << "Position " << i + 1  << " Information:" << endl << stat.redsStatistic[i];
		os << "----------------------------------------------" << endl;
		os << "Position " << i + 1  << " Out order information: " << endl << stat.redsStatisticOut[i];
		os << "----------------------------------------------" << endl;
		os << endl;
	}

	//Deal with blue
	os << "Blue Information: " << endl;
	os << stat.blueStatistic;

	os << "<------------------------------------------>" << endl;

	return os;
}

ULC_PositionInfo *ULC_Statistic::GetSortedRedInfo(int index)
{
	if (index < 0 || index >= UL_RED_BALLS_COUNT){
		return NULL;
	}

	return redsStatistic + index;
}

ULC_PositionInfo *ULC_Statistic::GetOutRedInfo(int index)
{
	if (index < 0 || index >= UL_RED_BALLS_COUNT){
		return NULL;
	}

	return redsStatisticOut + index;
}

ULC_PositionInfo *ULC_Statistic::GetBlueInfo()
{
	return &blueStatistic;
}
